<?php namespace xlx;
use \Workerman\Connection\TcpConnection;
use \Workerman\Connection\AsyncTcpConnection;
use \Workerman\Events\EventInterface;
 
class GenServer{  
    public static function start($aton) {
        \xlx\Process::spawn(function()use($aton){
            yield GenServer::start_server($aton);
        }); 
    }
    public static function start_server($return_coid) {   
        $actor = new \xlx\AChannel();//消息管道 
        yield $actor->register('genserver');
        yield $actor->send($return_coid,$actor->self());
        while(1){
            //craete server;
            $data = yield $actor->receive(function($data){
                //0:class, 1:method , 2:option , 3:return_coid(registerid)
                if(\method_exists($data[0],$data[1]))return $data;
            });   
            \xlx\co([$data[0],$data[1]],$actor,$data[2],$data[3])->then(null,function(){
                //error
            }); 
        }   
    } 
    public static function getChannel(){
        static $channel;
        if(empty( $channel )){
             $channel  = new \xlx\AChannel();//gen公告消息管道   
        }
        return $channel;
    } 
} 

class Pool {
    public static $childs=[];//[coid,...]
    public static function start($opt=[]) {
        return \xlx\co(function(){
            $channel = GenServer::getChannel();
            GenServer::start($channel->self());
            $gcoid = yield $channel->receive();
            $opt['class'] = static::class;
            $channel->send($gcoid,[static::class,'init',$opt, $channel->self() ]);
            return $channel->receive();
        });
    }
    public static function init($actor,$opt,$return_coid){  
        $pool = new \xlx\AChannel();//
        $actor->send($return_coid,$pool->self());
        yield $pool->register($opt['class']); 

        //message send
        $pool->send($pool->self(),['START']);
        //supervisor 
        while(1){ 
            yield $pool->receive(function($data)use($pool,$opt){
                if('EXIT'==$data[0]){ 
                    $index = \array_search($data[1] , self::$childs);
                    if($index!==false){ 
                        unset(self::$childs[$index]);
                    }
                    $data[0]=='START';
                }
                if('START'==$data[0]){  
                    return \xlx\co(function()use($pool,$opt){ 
                        while(count(self::$childs)<2){  
                            \xlx\Process::spawn(function()use($opt){  
                                $child = new AChannel();
                                $child->link($opt['class']);
                                $child->send($opt['class'],$child->self());
                                yield \xlx\co([$opt['class'],'initchild'],$child,$opt); 
                            });
                            self::$childs[] = yield $pool->receive();
                        }   
                    });
                } 
                if(true){ 
                    $index = array_rand(self::$childs);
                    $pool->send(self::$childs[$index],$data);
                    return true;
                }   
            }); 
        }   
    }
    public static function initchild($child,$opt){ 
    }
}
 
class Task extends Pool{  

    public static function initchild($child,$opt){
        while(1){
            $data = yield $child->receive(function($data) {return $data;}); 
            switch($data[0]){
                case 'eval':  
                    \xlx\co(\unserialize($data[1]))->then(function($return)use($data,$child){
                        $child->send($data[2],['eval',$return]);
                    }); 
                    break;
            }  
        } 
    }
    public static function run($fn) {  
        $channel = GenServer::getChannel();
        $fn = serialize(new \Opis\Closure\SerializableClosure($fn));
        $channel->send(static::class,['eval',$fn, $channel->self() ]);
        return $channel->receive(function($d){
            if(is_array($d) && 'eval'==$d[0]){
                return $d[1];
            }
        }); 
    } 
}

 
class TcpGateway extends Pool{
    public static function init($actor,$opt,$return_coid){  
        $pool = new \xlx\AChannel();//
        $actor->send($return_coid,$pool->self());
        yield $pool->register($opt['class']); 

        //message send
        $pool->send($pool->self(),['START']);
        //supervisor 
        while(1){ 
            yield $pool->receive(function($data)use($pool,$opt){
                if('EXIT'==$data[0]){ 
                    $index = \array_search($data[1] , self::$childs);
                    if($index!==false){ 
                        unset(self::$childs[$index]);
                    }
                    $data[0]=='START';
                }
                if('START'==$data[0]){  
                    return \xlx\co(function()use($pool,$opt){ 
                        while(count(self::$childs)<2){  
                            \xlx\Process::spawn(function()use($opt){  
                                $child = new AChannel();
                                $child->link($opt['class']);
                                $child->send($opt['class'],$child->self());
                                yield \xlx\co([$opt['class'],'initchild'],$child,$opt); 
                            });
                            self::$childs[] = yield $pool->receive();
                        }   
                    });
                } 
                if(true){ 
                    $index = array_rand(self::$childs);
                    $pool->send(self::$childs[$index],$data);
                    return true;
                }   
            }); 
        }   
    }
    
    public static function initchild($child,$opt){
        while(1){
            $data = yield $child->receive(function($data) {return $data;}); 
            switch($data[0]){
                case 'eval':  
                    \xlx\co(\unserialize($data[1]))->then(function($return)use($data,$child){
                        $child->send($data[2],['eval',$return]);
                    }); 
                    break;
            }  
        } 
    }


    public function __construct(){ 
        //stream_socket_server('worker://[...]') >
        //  stream_socket_accept($client) > 
        //      self::$conns->send($opt);
        //  ^
        //^   
    }

    public function child(){
        
        //stream_socket_server('worker://[len,nlen,name,...]') >
        //  stream_socket_accept($worker) >
        //      self::$conns[]=$worker
        //  ^
        //^
        //$event->add(1,Timer, >
        //  unset(self::$conns);
        //^)
    }
}
class Worker extends Pool{
    public function __construct(){
        //stream_socket_client('gateframe://[len,nlen,name,...]') >
        //  
    }
    public function onMessage($stream){

    }
}
 

// class Gateway extends Worker{

//     public function init($event,$addr,$opt){ 
//         $server = \stream_socket_server($opt['addr'], $errno, $errstr);
//         $event->add($server, EventInterface::EV_READ, function ($socket)use($class,$opt) {
//             $socket = \stream_socket_accept($socket, 0, $addr);
//             $conn = new TcpConnection($socket,$opt['addr']);
//             if(isset($opt['protocol'])){
//                 $conn->protocol=$opt['protocol'];
//             } 
//             \xlx\co([$this,'onConnect'],$conn);
//         }); 
//     } 
//     public function onConnect($conn){

//     }
// }

// class Ping extends Worker{
//     public function init($event,$addr,$opt ){ 
//         echo "ping:init\n";
//     }  
// }
// class Gateway extends Worker{
    
// }
// class Actor extends Worker{
    
// }